//Design by Gontract
//
//dbgo(1) is a proxy for the Go compiler that extracts simple contracts from a
//function's documentation and inserts informative print traces when the
//contracts are violated. You may specify the compiler to proxy with -5 or -6
//-8 to proxy 5g(1) or 6g(1) or 8g(1) respectively. Otherwise, the appropriate
//compiler for the current platform is chosen.
//
//Options marked with a * are passed directly to the proxied compiler.
//
//SYNTAX
//
//Contracts are specified in the documentation of functions and methods.
//
//There are four kinds of contracts: precondition, postcondition, invariant,
//and panic.
//
//Preconditions are checked upon entry into the function. Postconditions are
//checked on normal function exit. Invariants are checked in both cases. Panics
//are checked iff the function panics.
//
//Contracts are specified as Go expressions that contain any
//combination of the named receiver, named parameters, or named results.
//
//Contracts start with the kind they are, followed directly by a colon, :, and
//one or more boolean expressions on the intervening lines, which are terminated
//by a blank line or the end of the comment:
//
//	//
//	//precondition: expr0
//	//
//	//postcondition: expr1
//	//	expr2
//	//
//	//panic:
//	//	expr3
//	//	expr4
//
//SEMANTICS
//
//
//
//EXAMPLE
//
//test.go:
//
//	//invariant: self != nil
//	func (self *T) m() { ... }
//	func main() {
//		var t *T
//		t.m()
//	}
//
//compiling and running (abbreviated output)
//	$ dbgo test.go && 6l test.6 && ./6.out
//	main violated self != nil on entry to m with:
//	Receiver: nil
package main

import (
	"os"
	"log"
	"exec"
	"io/ioutil"
)

func main() {
	log.SetFlags(0)

	save, args, infiles, err := parse_args()
	check(err)

	addfile := func(f string) {
		args = append(args, f)
	}

	//process files
	transmap := []*JunkDrawer{}
	pkg := ""

	for _, fname := range infiles {
		file, err := parse(fname)
		check(err)

		funcs, err := scrape(file)
		check(err)
		//no functions in this file, just passthrough
		if len(funcs) == 0 {
			addfile(fname)
			continue
		}

		//this is only the funcs with contracts
		cfuncs, err := contracts(file.name, funcs)
		check(err)

		//no contracts on any funcs in this file, just passthrough
		if len(cfuncs) == 0 {
			addfile(fname)
			continue
		}

		//we know we are modifying this file, so we have to save all of its
		//funcs so we can add them to the linenumber translation map
		transmap = append(transmap, funcs...)

		check(rewrite(cfuncs))

		file.ast = Issue1505(file.fs, file.ast) //XXX dirty hack should die

		//this fname is not the name the file was opened with
		fname, err := file.write()
		check(err)
		if save {
			defer os.Remove(fname)
		}
		addfile(fname)

		//we need this to generate the dbgo_.go file
		if pkg == "" {
			pkg = file.ast.Name.String()
		}
	}

	//pkg won't be set if no files had contracts, so if it is we need to
	//add a file to the project with all the additional junk
	if pkg != "" {
		scratch := format_template(pkg, transmap)
		check(ioutil.WriteFile(INCLUDE, scratch, 0666))
		if save {
			defer os.Remove(INCLUDE)
		}
		addfile(INCLUDE)
	}

	//proxy for the compiler
	check(run(args))
}

func check(err os.Error) {
	if err != nil {
		log.Fatalln(err)
	}
}

func run(args []string) (err os.Error) {
	c := exec.Command(args[0], args[1:]...)
	c.Stdin = os.Stdin
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr
	return c.Run()
}
